package com.xms.sdk.query;

import cn.hutool.core.lang.Assert;
import com.xms.core.model.PagedList;
import com.xms.datacore.SqlDescriptor;
import com.xms.datacore.provider.IDataRepositoryBase;
import com.xms.schema.entity.Entity;
import com.xms.sdk.core.DynamicData;
import com.xms.sdk.core.RetrieverParameter;
import com.xms.sdk.core.query.*;
import com.xms.sdk.extensions.IQueryMetadataFinder;
import com.xms.utils.CollectionUtil;
import com.xms.utils.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.LinkedList;
import java.util.List;
import java.util.Locale;

/**
 * <p>
 * QueryByAttributeResolver
 * </p>
 *
 * @author migo
 * @since 2021/9/4
 */
@Service
public class QueryByAttributeResolver implements IQueryResolver {
    private Logger _logger = LoggerFactory.getLogger(getClass());
    private IDataRepositoryBase _dataRepositoryBase;
    private IQueryMetadataFinder _queryMetadataFinder;

    @Autowired
    public void QueryByAttributeResolver(IDataRepositoryBase dataRepositoryBase,
                                         IQueryMetadataFinder queryMetadataFinder) {
        _dataRepositoryBase = dataRepositoryBase;
        _queryMetadataFinder = queryMetadataFinder;
    }

    /*public ICurrentUser User;

    private QueryByAttribute _queryByAttribute;

    @Override
    public IQueryResolver init(QueryBase query) {
        _queryByAttribute = (QueryByAttribute) query;
        _entityList = new LinkedList<>();
        _attributeList = new LinkedList<>();
        _relationshipList = new LinkedList<>();
        _parameters = new QueryParameters();
        if (query != null && !query.EntityName.isEmpty()) {
            List<EntityDescriptor> metadatas = _queryMetadataFinder.getAll(query);
            for (EntityDescriptor ent : metadatas) {
                _entityList.add(ent.entity);
                if (CollectionUtil.notEmpty(ent.attributes)) {
                    _attributeList.addAll(ent.attributes);
                }
                if (CollectionUtil.notEmpty(ent.relationships)) {
                    _relationshipList.addAll(ent.relationships);
                }
            }
        }
        return this;
    }*/


    private QueryResult<PagedList<DynamicData>> buildPagedQueryResult(QueryBase query) {
        Assert.notNull(query);
        Assert.notBlank(query.EntityName);
        QueryResult<PagedList<DynamicData>> result = new QueryResult<>();
        QueryMetadataDescriptor metadatas = _queryMetadataFinder.getAll(query);
        result.metadataDescriptor = metadatas;
        return result;
    }

    private QueryResult<List<DynamicData>> buildListQueryResult(QueryBase query) {
        Assert.notNull(query);
        Assert.notBlank(query.EntityName);
        QueryResult<List<DynamicData>> result = new QueryResult<>();
        QueryMetadataDescriptor metadatas = _queryMetadataFinder.getAll(query);
        result.metadataDescriptor = metadatas;
        return result;
    }

    private QueryResult<DynamicData> buildQueryResult(QueryBase query) {
        Assert.notNull(query);
        Assert.notBlank(query.EntityName);
        QueryResult<DynamicData> result = new QueryResult<>();
        result.metadataDescriptor = this.getMetadatas(query);
        return result;
    }

    public QueryMetadataDescriptor getMetadatas(QueryBase query) {
        return _queryMetadataFinder.getAll(query);
    }

    //@Override
    public String getAliasJoiner() {
        return ".";
    }

    /*@Override
    public QueryBase getQueryObject() {
        return _queryByAttribute;
    }

    private List<Entity> _entityList;

    private List<Attribute> _attributeList;

    public List<RelationShip> _relationshipList;

    public QueryParameters _parameters;

    @Override
    public Entity getMainEntity() {
        if (CollectionUtil.notEmpty(_entityList)) {
            return _entityList.get(0);
        }
        return null;
    }

    @Override
    public List<AttributeAlias> getAttributeAliasList() {
        return null;
    }

    @Override
    public List<Attribute> getAttributeList() {
        return _attributeList;
    }

    @Override
    public List<Entity> getEntityList() {
        return _entityList;
    }

    @Override
    public QueryParameters getParameters() {
        return _parameters;
    }

    @Override
    public List<RelationShip> getRelationShipList() {
        return _relationshipList;
    }*/

    /*@Override
    public QueryResult<DynamicData> find(RetrieverParameter parameters) throws Exception {
        QueryResult<DynamicData> result = this.buildQueryResult(parameters.getQuery());
        SqlDescriptor sql = toSqlString(parameters, result.metadataDescriptor);
        result.data = _dataRepositoryBase.one(sql);
        return result;
    }

    @Override
    public QueryResult<List<DynamicData>> query(RetrieverParameter parameters) throws SQLException {
        QueryResult<List<DynamicData>> result = this.buildListQueryResult(parameters.getQuery());
        SqlDescriptor sql = toSqlString(parameters, result.metadataDescriptor);
        result.data = _dataRepositoryBase.query(sql);
        return result;
    }

    @Override
    public QueryResult<PagedList<DynamicData>> queryPaged(RetrieverParameter parameters) throws Exception {
        QueryResult<PagedList<DynamicData>> result = this.buildPagedQueryResult(parameters.getQuery());
        QueryExpression queryExpression = (QueryExpression) parameters.getQuery();
        SqlDescriptor sql = toSqlString(parameters, result.metadataDescriptor);
        result.data = _dataRepositoryBase.page(queryExpression.PageInfo.PageNumber, queryExpression.PageInfo.PageSize, sql);
        return result;
    }*/

    @Override
    public SqlDescriptor toSqlString(RetrieverParameter retrieverParameter, QueryMetadataDescriptor metadataDescriptor) {
        QueryByAttribute queryByAttribute = (QueryByAttribute) retrieverParameter.getQuery();
        Entity mainEntity = metadataDescriptor.entityList.stream().filter(x -> x.getName().equalsIgnoreCase(queryByAttribute.EntityName)).findFirst().get();
        if (!queryByAttribute.ColumnSet.AllColumns
                && !queryByAttribute.ColumnSet.Columns.stream().anyMatch(x -> x.equalsIgnoreCase(mainEntity.getName() + "id"))) {
            queryByAttribute.ColumnSet.addColumn(mainEntity.getName() + "id");
        }
        QueryParameters sqlParameters = new QueryParameters();
        List<String> columns = new LinkedList<>();
        List<String> filters = new LinkedList<>();
        List<String> orders = new LinkedList<>();
        //columns
        if (queryByAttribute.ColumnSet.AllColumns) {
            columns.add("*");
        } else {
            for (String item : queryByAttribute.ColumnSet.Columns) {
                columns.add(item);
            }
        }
        //filters
        if (CollectionUtil.notEmpty(queryByAttribute.Attributes)) {
            for (String item : queryByAttribute.Attributes) {
                filters.add(String.format("%s.%s=?", mainEntity.getName(), item));
            }
        }
        if (CollectionUtil.notEmpty(queryByAttribute.Values)) {
            sqlParameters.args = (queryByAttribute.Values);
        }

        //orders
        if (CollectionUtil.notEmpty(queryByAttribute.Orders)) {
            for (OrderExpression ord : queryByAttribute.Orders) {
                orders.add(mainEntity.getName() + "." + ord.AttributeName + " " + (ord.OrderType == OrderType.Descending ? " DESC" : ""));
            }
        }
        String s = String.format("SELECT %s FROM %sview AS %s %s %s"
                , String.join(",", columns).toLowerCase(Locale.ROOT)
                , mainEntity.getName(), mainEntity.getName()
                , CollectionUtil.notEmpty(filters) ? " WHERE " + String.join(" AND ", filters) : ""
                , CollectionUtil.notEmpty(orders) ? " ORDER BY " + String.join(",", orders) : "");

        _logger.info("finalsql is: {}", s);
        if (sqlParameters.args.size() > 0)
            _logger.info("finalargs is: {}", StringUtil.join(sqlParameters.args, ", "));

        return CollectionUtil.notEmpty(sqlParameters.args) ?
                new SqlDescriptor(s, sqlParameters.args) : new SqlDescriptor(s);
    }
}
